package com.davfx.httpnio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

final class BasicListening implements Listening {
	private final Worker worker;
	private boolean closed = false;
	private ServerSocketChannel channel = null;
	private ConnectionHandler acceptHandler = null;
	private SelectionKey selectionKey = null;
	private Selector selector = null;
	private IOException failed = null;
	
	public BasicListening(Worker worker) {
		this.worker = worker;
	}
	
	// called in worker thread
	void create(Selector selector, String host, int port) {
		try {
			channel = ServerSocketChannel.open();
			channel.configureBlocking(false);
			channel.socket().bind(new InetSocketAddress(host, port));
			selectionKey = channel.register(selector, 0);
			this.selector = selector;
			if (acceptHandler != null) {
				attach();
			}
		} catch (IOException e) {
			failed = e;
			if (acceptHandler != null) {
				acceptHandler.failed(e);
			}
		}
	}
	
	private void attach() {
		selectionKey.attach(new SelectionKeyVisitor() {
			@Override
			public void visit(SelectionKey key) {
				if (!key.isAcceptable()) {
					return;
				}
				ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
				try {
					SocketChannel outboundChannel = ssc.accept();
					try {
						outboundChannel.configureBlocking(false);
						BasicConnection connection = new BasicConnection(worker);
						connection.connected(selector, outboundChannel);
						acceptHandler.handle(connection);
					} catch (IOException e) {
						try {
							outboundChannel.close();
						} catch (IOException ee) {
						}
					}
				} catch (IOException e) {
				}
			}
		});
		selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_ACCEPT);
	}
	
	@Override
	public void listen(final ConnectionHandler handler) {
		worker.add(new SelectorVisitor() {
			@Override
			public void visit(final Selector selector) {
				if (failed != null) {
					handler.failed(failed);
					return;
				}
				
				acceptHandler = handler;
				if (selectionKey != null) {
					attach();
				}
			}
		});
	}

	@Override
	public void close() {
		worker.add(new SelectorVisitor() {
			@Override
			public void visit(final Selector selector) {
				if (closed) {
					return;
				}
				if (channel != null) {
					try {
						channel.close();
					} catch (IOException ee) {
					}
				}
				closed = true;
			}
		});
	}

	// called in worker thread
	boolean isClosed() {
		return closed;
	}
}